#include "frontend/token.h"

#include <map>
#include <iostream>

namespace mspc {

std::string Token::to_string() const {
    static std::map<TokenType, std::string> type_names = {
        {TokenType::KW_SYNTAX, "KW_SYNTAX"},
        {TokenType::KW_PACKAGE, "KW_PACKAGE"},
        {TokenType::KW_IMPORT, "KW_IMPORT"},
        {TokenType::KW_ENUM, "KW_ENUM"},
        {TokenType::KW_STRUCT, "KW_STRUCT"},
        {TokenType::KW_MESSAGE, "KW_MESSAGE"},
        {TokenType::KW_ID, "KW_ID"},
        {TokenType::KW_REPEATED, "KW_REPEATED"},
        {TokenType::TYPE_BOOL, "TYPE_BOOL"},
        {TokenType::TYPE_CHAR, "TYPE_CHAR"},
        {TokenType::TYPE_INT8, "TYPE_INT8"},
        {TokenType::TYPE_UINT8, "TYPE_UINT8"},
        {TokenType::TYPE_INT16, "TYPE_INT16"},
        {TokenType::TYPE_UINT16, "TYPE_UINT16"},
        {TokenType::TYPE_INT32, "TYPE_INT32"},
        {TokenType::TYPE_UINT32, "TYPE_UINT32"},
        {TokenType::TYPE_INT64, "TYPE_INT64"},
        {TokenType::TYPE_UINT64, "TYPE_UINT64"},
        {TokenType::TYPE_FLOAT, "TYPE_FLOAT"},
        {TokenType::TYPE_DOUBLE, "TYPE_DOUBLE"},
        {TokenType::TYPE_STRING, "TYPE_STRING"},
        {TokenType::TYPE_BYTES, "TYPE_BYTES"},
        {TokenType::LBRACE, "LBRACE"},
        {TokenType::RBRACE, "RBRACE"},
        {TokenType::LBRACKET, "LBRACKET"},
        {TokenType::RBRACKET, "RBRACKET"},
        {TokenType::EQUAL, "EQUAL"},
        {TokenType::LPAREN, "LPAREN"},
        {TokenType::RPAREN, "RPAREN"},
        {TokenType::SEMICOLON, "SEMICOLON"},
        {TokenType::PIPE, "PIPE"},
        {TokenType::COLON, "COLON"},
        {TokenType::COMMA, "COMMA"},
        {TokenType::LESS, "LESS"},
        {TokenType::GREATER, "GREATER"},
        {TokenType::IDENTIFIER, "IDENTIFIER"},
        {TokenType::NUMBER, "NUMBER"},
        {TokenType::STRING_LITERAL, "STRING"},
        {TokenType::END_OF_FILE, "END_OF_FILE"},
        {TokenType::ERROR, "ERROR"},
    };

    std::stringstream ss;
    ss << line << ":" << column << " ";
    ss << type_names.at(type) << "('" << value << "')";
    return ss.str();
}

std::unordered_map<std::string, TokenType> init_key_words_table() {
    std::unordered_map<std::string, TokenType> keywords = {
        {"syntax", TokenType::KW_SYNTAX}, {"package", TokenType::KW_PACKAGE},
        {"import", TokenType::KW_IMPORT}, {"enum", TokenType::KW_ENUM},
        {"struct", TokenType::KW_STRUCT}, {"message", TokenType::KW_MESSAGE},
        {"id", TokenType::KW_ID},         {"repeated", TokenType::KW_REPEATED},
    };
    return keywords;
}

std::map<TokenType, std::string> init_key_words_table2() {
    std::map<TokenType, std::string> keywords = {
        {TokenType::KW_SYNTAX, "syntax"}, {TokenType::KW_PACKAGE, "package"},
        {TokenType::KW_IMPORT, "import"}, {TokenType::KW_ENUM, "enum"},
        {TokenType::KW_STRUCT, "struct"}, {TokenType::KW_MESSAGE, "message"},
        {TokenType::KW_ID, "id"},         {TokenType::KW_REPEATED, "repeated"},
    };
    return keywords;
}

std::unordered_map<std::string, TokenType> init_type_words_table() {
    std::unordered_map<std::string, TokenType> typewords = {
        {"bool", TokenType::TYPE_BOOL},     {"char", TokenType::TYPE_CHAR},
        {"int8", TokenType::TYPE_INT8},     {"uint8", TokenType::TYPE_UINT8},
        {"int16", TokenType::TYPE_INT16},   {"uint16", TokenType::TYPE_UINT16},
        {"int32", TokenType::TYPE_INT32},   {"uint32", TokenType::TYPE_UINT32},
        {"int64", TokenType::TYPE_INT64},   {"uint64", TokenType::TYPE_UINT64},
        {"float", TokenType::TYPE_FLOAT},   {"double", TokenType::TYPE_DOUBLE},
        {"string", TokenType::TYPE_STRING}, {"bytes", TokenType::TYPE_BYTES},
    };
    return typewords;
}

std::map<TokenType, std::string> init_type_words_table2() {
    std::map<TokenType, std::string> typewords = {
        {TokenType::TYPE_BOOL, "bool"},     {TokenType::TYPE_CHAR, "char"},
        {TokenType::TYPE_INT8, "int8"},     {TokenType::TYPE_UINT8, "uint8"},
        {TokenType::TYPE_INT16, "int16"},   {TokenType::TYPE_UINT16, "uint16"},
        {TokenType::TYPE_INT32, "int32"},   {TokenType::TYPE_UINT32, "uint32"},
        {TokenType::TYPE_INT64, "int64"},   {TokenType::TYPE_UINT64, "uint64"},
        {TokenType::TYPE_FLOAT, "float"},   {TokenType::TYPE_DOUBLE, "double"},
        {TokenType::TYPE_STRING, "string"}, {TokenType::TYPE_BYTES, "bytes"},
    };
    return typewords;
}
////////////////////////////////////////
static const Token& get_eof_token() {
    static const Token eof(TokenType::END_OF_FILE, "", 0, 0);
    return eof;
}

static const Token& get_begin_token() {
    static const Token begin(TokenType::ERROR, "BEGIN", 0, 0);
    return begin;
}

TokenContainer::TokenContainer(const std::string& filename)
    : current_(0)
    , filename_(filename) {}

void TokenContainer::add_token(Token token) { tokens_.push_back(std::move(token)); }

const Token& TokenContainer::peek() const {
    if (current_ >= tokens_.size()) {
        return get_eof_token();
    }
    return tokens_[current_];
}

const Token& TokenContainer::next() {
    if (current_ < tokens_.size()) {
        current_++;
    }
    return previous();
}

const Token& TokenContainer::previous() const {
    if (current_ == 0) {
        return get_begin_token();
    }
    return tokens_[current_ - 1];
}
const Token& TokenContainer::advance() {
    if (!is_at_end()) current_++;
    return previous();
}
bool TokenContainer::is_at_end() const {
    return current_ >= tokens_.size() || peek().type == TokenType::END_OF_FILE;
}

size_t TokenContainer::save_position() const { return current_; }
void TokenContainer::restore_position(size_t position) { current_ = position; }
size_t TokenContainer::size() const { return tokens_.size(); }
bool TokenContainer::empty() const { return tokens_.empty(); }
void TokenContainer::reset() { current_ = 0; }
const std::vector<Token>& TokenContainer::tokens() const { return tokens_; }
const std::string& TokenContainer::filename() const { return filename_; }
std::string TokenContainer::format_error(size_t index, const std::string& message) const {
    const auto& token = tokens_[index];
    return filename_ + ":" + std::to_string(token.line) + ":" + std::to_string(token.column) +
           ": " + message;
}
std::string TokenContainer::format_error(const std::string& message) const {
    const auto& token = tokens_[current_];
    return filename_ + ":" + std::to_string(token.line) + ":" + std::to_string(token.column) +
           ": " + message;
}

size_t TokenContainer::current() const { return current_; }

void TokenContainer::print() const {
    for (const auto& token : tokens_) {
        std::cout << token.to_string() << std::endl;
    }
}

} // namespace mspc
